/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Compile.

    Maxsi Compile is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Compile is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Compile.  If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiCompileContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiCompileContributors.h. For more information please see
	MaxsiCompileContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]


	MaxsiCompile.exe
	A system to control MaxsiCompile Systems.

	CompileMap.cpp
	Triggers the actual compiling

****************************************************************************/

#include "MaxsiEngine.h"
#include <direct.h>
#include <iostream> // Remove me
#include "CompileMap.h"

//=============================================================================
//	Shared data table
//=============================================================================
MESTR*	SteamDir				=	NULL;
MESTR*	SteamUsername			=	NULL;
MESTR*	SDKEngineVersion		=	NULL;
MESTR*	ModGameFolder			=	NULL;
MESTR*	SDKGameFolder			=	NULL;
MESTR*	ModMapSrcFolder			=	NULL;
MESTR*	ModMapsFolder			=	NULL;

//=============================================================================
//	ModMeta: A meta class for a mod with all the paths needed
//=============================================================================

ModMeta::ModMeta()
{
	GameDir				=	NULL;
	GameExe				=	NULL;
	BSP					=	NULL;
	Vis					=	NULL;
	Light				=	NULL;
	GameExeDir			=	NULL;
	MapDir				=	NULL;
	BSPDir				=	NULL;

	SDKEngineVersion	=	NULL;
	SDKEngineRoot		=	NULL;
}

ModMeta::~ModMeta()
{
	IFDEL( GameDir );
	IFDEL( GameExe );
	IFDEL( BSP );
	IFDEL( Vis );
	IFDEL( Light );
	IFDEL( GameExeDir );
	IFDEL( MapDir );
	IFDEL( BSPDir );

	IFDEL( SDKEngineVersion );
	IFDEL( SDKEngineRoot );
}

//=============================================================================
//	MapMeta: A meta class for a map with all the paths needed
//=============================================================================

MapMeta::MapMeta()
{
	MapName		=	NULL;
	FileName	=	NULL;
	Mod			=	NULL;
}


MapMeta::~MapMeta()
{
	IFDEL( MapName );
	IFDEL( FileName )
}

//=============================================================================
//	Configuration: A list of all the tools and their state needed for a compile
//=============================================================================

Configuration::Configuration()
{
	NumConfigurations	=	0;
	Configurations		=	NULL;
}

Configuration::~Configuration()
{
	if ( Configurations )
	{
		for ( size_t I = 0; I < NumConfigurations; I++ )
		{
			if (Configurations[I]) {  delete Configurations[I]; }
		}
		delete[] Configurations;
	}
}

//=============================================================================
//	ToolConfiguration: A class holding all the variables used by a tool
//=============================================================================

ToolConfiguration::ToolConfiguration()
{
	Executable			=	NULL;
	AdditionalArguments	=	NULL;
	WorkingDir			=	NULL;
}

ToolConfiguration::~ToolConfiguration()
{
	IFDEL( Executable );
	IFDEL( AdditionalArguments );
	IFDEL( WorkingDir );
}

MESTR* ToolConfiguration::CreateCommandLine(MapMeta* Input)
{
	MESTR*	ParsedMapFile		=	BuildString(1,Input->FileName);
	MESTR*	ParsedMapFileNoExt	=	RemoveFilePathExt(ParsedMapFile);

	MESTR*	ParsedExecutable	=	BuildString(1,Executable);

			ParsedExecutable	=	StringReplace(_MESTR("%BSP%"),Input->Mod->BSP,ParsedExecutable);
			ParsedExecutable	=	StringReplace(_MESTR("%VIS%"),Input->Mod->Vis,ParsedExecutable);
			ParsedExecutable	=	StringReplace(_MESTR("%LIGHT%"),Input->Mod->Light,ParsedExecutable);

	MESTR*	ParsedArguments		=	BuildString(1, AdditionalArguments);

			ParsedArguments		=	StringReplace(_MESTR("%GAMEDIR%"),Input->Mod->GameDir,ParsedArguments);
			ParsedArguments		=	StringReplace(_MESTR("%VMFNOEXT%"),ParsedMapFileNoExt,ParsedArguments);

	MESTR*	Result				=	BuildString(4,"\"", ParsedExecutable, "\" ", ParsedArguments);

	IFDEL(ParsedArguments);
	IFDEL(ParsedExecutable);
	IFDEL(ParsedMapFileNoExt);
	IFDEL(ParsedMapFile);
	IFDEL(ParsedArguments);

	return Result;
}

MESTR* ToolConfiguration::GetWorkingDir(MapMeta *Input)
{
	if ( Input == NULL ) { return NULL; }
	
	return BuildString(1,Input->Mod->SDKEngineRoot);
}

//=============================================================================
//	CompileRequest: Contains all the commands used to compile a specific map
//=============================================================================

CompileRequest::CompileRequest()
{
	Map					=	NULL;
	NumCommands			=	0;
	Commands			=	NULL;
	WorkingDirs			=	NULL;
}

CompileRequest::~CompileRequest()
{
	if ( Commands )
	{
		for ( size_t I = 0; I < NumCommands; I++ )
		{
			IFDEL( Commands[I] );
			IFDEL( WorkingDirs[I] );
		}
		delete[] Commands;
	}
}

//=============================================================================
//	Functions that compile stuff
//=============================================================================

CompileRequest*		CreateCompileRequest(MapMeta* Map, Configuration* Config)
{
	if ( !Map || !Config ) { return NULL; }

	CompileRequest*		Result	=	new CompileRequest;

	if ( Result == NULL ) { return NULL; }

	Result->Map					=	Map;
	Result->NumCommands			=	Config->NumConfigurations;
	Result->Commands			=	new MESTR*[Result->NumCommands];
	Result->WorkingDirs			=	new MESTR*[Result->NumCommands];

	if ( Result->Commands == NULL ) { delete Result; return NULL; }
	if ( Result->WorkingDirs == NULL ) { delete[] Result->Commands; delete Result; return NULL; }

	for ( size_t I = 0; I < Result->NumCommands; I++ )
	{
		Result->Commands[I]		=	Config->Configurations[I]->CreateCommandLine(Map);
		Result->WorkingDirs[I]	=	Config->Configurations[I]->GetWorkingDir(Map);

		if ( Result->Commands[I] == NULL || Result->Commands[I] == NULL)
		{
			// Failure in the tool, abort the whole compile request generation. Clean up!
			for ( size_t N = 0; N < I; N++ )
			{
				IFDELSINGLE(Result->Commands[N]);
				IFDELSINGLE(Result->WorkingDirs[N]);
			}
			for ( size_t N = I; N < Result->NumCommands; N++ )
			{
				Result->Commands[N] = NULL;
				Result->WorkingDirs[N] = NULL;
			}
			delete Result;
			return NULL;
		}
	}
	
	return Result;
}

int		CompileMap(CompileRequest* Request)
{
	for ( size_t I = 0; I < Request->NumCommands; I++ )
	{
		if ( Request->Commands[I] )
		{
			ChangeDir(Request->WorkingDirs[I]);
			RunProcess(Request->Commands[I], NULL);
		}
	}

	return 0;
}

int		CompileMap(MESTR* MapName, MESTR* VBSPParam, MESTR* VVISParam, MESTR* VRadPass1Param, MESTR* VRadPass2Param)
{
	if ( !MapName || !SteamUsername || !SDKEngineVersion || !ModGameFolder || !SDKGameFolder || !ModMapSrcFolder || !ModMapsFolder ) { return 0; }

	MESTR*	AbsSteamDir			=	BuildString(1, SteamDir);
	MESTR*	AbsSteamApps		=	BuildString(2, SteamDir, "\\SteamApps");
	MESTR*	AbsUserSteamApps	=	BuildString(3, AbsSteamApps, "\\", SteamUsername);
	MESTR*	AbsSourceMods		=	BuildString(2, AbsSteamApps, "\\SourceMods");
	MESTR*	AbsSDKRoot			=	BuildString(2, AbsUserSteamApps, "\\SourceSDK\\bin");
	MESTR*	AbsSDKFolder		=	BuildString(4, AbsSDKRoot, "\\", SDKEngineVersion, "\\bin");
	MESTR*	AbsSDKWorkingDir	=	BuildString(3, AbsSDKRoot, "\\", SDKEngineVersion);

	MESTR*	AbsVBSP				=	BuildString(2, AbsSDKFolder, "\\vbsp.exe");
	MESTR*	AbsVVIS				=	BuildString(2, AbsSDKFolder, "\\vvis.exe");
	MESTR*	AbsVRAD				=	BuildString(2, AbsSDKFolder, "\\vrad.exe");
	MESTR*	AbsModGameFolder	=	BuildString(3, AbsSourceMods, "\\", ModGameFolder);
	MESTR*	AbsSDKGameFolder	=	BuildString(3, AbsSourceMods, "\\", SDKGameFolder);

	MESTR*	AbsModMapSrcFolder	=	BuildString(3, AbsModGameFolder, "\\", ModMapSrcFolder);
	MESTR*	AbsModMapsFolder	=	BuildString(3, AbsModGameFolder, "\\", ModMapsFolder);

	MESTR*	ToolCmdLineAppend	=	BuildString(7, " -game \"",AbsSDKGameFolder,"\" \"",AbsModMapSrcFolder,"\\",MapName,"\"");

	MESTR*	Commands[4]			=
		{
			BuildString(5, "\"", AbsVBSP, "\" ", VBSPParam, ToolCmdLineAppend),
			BuildString(5, "\"", AbsVVIS, "\" ", VVISParam, ToolCmdLineAppend),
			BuildString(5, "\"", AbsVRAD, "\" ", VRadPass1Param, ToolCmdLineAppend),
			BuildString(5, "\"", AbsVRAD, "\" ", VRadPass2Param, ToolCmdLineAppend),
		};

	ChangeDir( AbsSDKWorkingDir );

	// Did all memory allocations go well?
	if ( AbsSteamDir && AbsSteamApps && AbsUserSteamApps && AbsSourceMods && AbsSDKRoot && AbsSDKFolder &&
		 AbsSDKWorkingDir && AbsVBSP && AbsVVIS && AbsVRAD && AbsModGameFolder && AbsSDKGameFolder &&
		 AbsModMapSrcFolder && AbsModMapsFolder && ToolCmdLineAppend )
	{
		for ( size_t I = 0; I < 4; I++ )
		{
			if ( Commands[I] )
			{
				LPSTARTUPINFOM si = new _STARTUPINFOM;
				LPPROCESS_INFORMATION pi = new _PROCESS_INFORMATION;

				ZeroMemory( si, sizeof(_STARTUPINFOA) );
				si->cb = sizeof(si);
				ZeroMemory( pi, sizeof(_PROCESS_INFORMATION) );

				// Start the child process. 
				if( CreateProcessM( NULL,   // No module name (use command line)
					Commands[I],        // Command line
					NULL,           // Process handle not inheritable
					NULL,           // Thread handle not inheritable
					FALSE,          // Set handle inheritance to FALSE
					0,              // No creation flags
					NULL,           // Use parent's environment block
					NULL,           // Use parent's starting directory 
					si,            // Pointer to STARTUPINFO structure
					pi )           // Pointer to PROCESS_INFORMATION structure
				) 
				{
					Sleep(1000);
					if ( AttachConsole( pi->dwProcessId ) == TRUE )
					{
						HANDLE	hStdout	=	GetStdHandle(STD_OUTPUT_HANDLE); 

						if ( hStdout != INVALID_HANDLE_VALUE && hStdout != NULL )
						{
							SMALL_RECT	srctReadRect; 
							SMALL_RECT	srctWriteRect;
							COORD		coordBufSize; 
							COORD		coordBufCoord; 
							CHAR_INFO	MemoryScreen[10*80];
							char		Line[10*81];
							memset(MemoryScreen, 0, sizeof(CHAR_INFO)*2*80);

							// Set the source rectangle. 

							srctReadRect.Top = 0;    // top left: row 0, col 0 
							srctReadRect.Left = 0; 
							srctReadRect.Bottom = 9; // bot. right: row 2, col 79 
							srctReadRect.Right = 79; 

							// The temporary buffer size is 2 rows x 80 columns. 

							coordBufSize.Y = 10; 
							coordBufSize.X = 80;

							// The top left destination cell of the temporary buffer is 
							// row 0, col 0. 

							coordBufCoord.X = 0; 
							coordBufCoord.Y = 0; 

							// Copy the block from the screen buffer to the temp. buffer. 

							bool	StillDoingThisLine = true;

							while ( ReadConsoleOutputA( 
								hStdout,        // screen buffer to read from 
								MemoryScreen,      // buffer to copy into 
								coordBufSize,   // col-row size of chiBuffer 
								coordBufCoord,  // top left dest. cell in chiBuffer 
								&srctReadRect) ) // screen buffer source rectangle 
							{
								Sleep(1);
								for ( size_t I = 1*80; I < 10*80; I++ )
								{
									Line[I+I/80]		=	MemoryScreen[I].Char.AsciiChar;
									if ( Line[I+I/80] != ' ' )
									{
										StillDoingThisLine = false;
										break;
									}
								}

								if ( StillDoingThisLine == false )
								{
									for ( size_t I = 0*80; I < 1*80; I++ )
									{
										Line[I+I/80]		=	MemoryScreen[I].Char.AsciiChar;
									}
									Line[1*81-1]		=	0;
									
									std::cout << Line;

									srctReadRect.Bottom++;
									srctReadRect.Top++;
									StillDoingThisLine	=	true;
								}
								else
								{
									for ( size_t I = 0*80; I < 1*80; I++ )
									{
										Line[I+I/80]		=	MemoryScreen[I].Char.AsciiChar;
									}

									FreeConsole();
									AttachConsole( pi->dwProcessId );
								}

								SetConsoleTitleA(Line);

								srctReadRect.Left = 0;
								srctReadRect.Right = 79;								
							}

							int LastError = GetLastError();

							LastError = 1337;

							//char*	OutBuffer[80+1];
							//DWORD	NumberOfBytesRead;
							//while (	ReadFile(hStdout, OutBuffer, 80, &NumberOfBytesRead, NULL)  )
							//{
							//	std::cout << OutBuffer;
							//}
							//int ReadFileError = GetLastError();

							//int leet = 1338+6-7;
						}
						else
						{
							int LastError = GetLastError();

							__debugbreak();
						}
					}
					else
					{
						int LastError = GetLastError();

						__debugbreak();
					}

					WaitForSingleObject( pi->hProcess, INFINITE );

					CloseHandle( pi->hProcess );
					CloseHandle( pi->hThread );
				}
				else
				{
					// Error, complain or something.
				}

				delete pi;
				delete si;
			}
		}		
	}

	IFDEL(Commands[0]);
	IFDEL(Commands[1]);
	IFDEL(Commands[2]);
	IFDEL(Commands[3]);
	IFDEL(AbsSteamDir);
	IFDEL(AbsSteamApps);
	IFDEL(AbsUserSteamApps);
	IFDEL(AbsSourceMods);
	IFDEL(AbsSDKRoot);
	IFDEL(AbsSDKFolder);
	IFDEL(AbsSDKWorkingDir);
	IFDEL(AbsVBSP);
	IFDEL(AbsVVIS);
	IFDEL(AbsVRAD);
	IFDEL(AbsModGameFolder);
	IFDEL(AbsSDKGameFolder);
	IFDEL(AbsModMapSrcFolder);
	IFDEL(AbsModMapsFolder);	

	return 1;
}